package com.briup.demo.web;

import com.briup.demo.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

/**
 * 在SpringMVC框架下，真正负责处理请求的组件为Controller或Handler；
 * 在这种情况下，我们就需要让我们自定义的XxxController被识别为SpringMVC的Controller组件；
 * <p>
 * 在Java中，如何让JVM识别到某一个类是另一个类，即表示 A is a B的关系
 * 1、使用继承关系，即A类继承B类，那么A类就表示为B类的子类，即A is a B的关系
 * 2、使用实现关系，即A类实现B接口，那么A类就表示为B接口的实现类，即A is a B的关系
 * 3、使用注解关系，即在A类中添加了某一个注解，那么在其对应的框架中，
 * 就会识别到A类是属于该注解标注的类型，具体是哪一个类型，还需要通过对应框架进行判断解析
 * 4、使用配置关系，即在A类中添加了某一个配置，那么在其对应的框架中，
 * 就会识别到A类是属于该配置标注的类型，具体是哪一个类型，还需要通过对应框架进行判断解析
 * <p>
 * 在本案例中，我们使用了注解关系，即添加了@Controller注解，
 * 那么在SpringMVC框架中，就会识别到IndexController类是属于Controller类型的组件；
 * 同时@Controller注解还是@Component注解的衍生注解，即SpringIOC容器在扫描到该类时，
 * 就会自动创建该类的对象，并注册到SpringIOC容器中；
 */
@Controller
public class IndexController {

    /*
    在Controller类中，主要职责为绑定请求路径和处理方法；
     然后当前端有请求传入时，就会根据@RequestMapping注解中的路径，
     以及请求方式等内容，判断我们要执行的是哪一个处理方法，
     并将请求参数绑定到该方法的参数中，最后调用该方法来处理请求；

     在这一过程中需要注意，因为请求参数在解析时基本上都是字符串类型，
     所以在绑定参数时，需要注意参数的类型转换问题；
     如存在一个为null或没有携带的参数age，那么在绑定参数并转换为int类型时，
     就会出现问题，导致出现抛出异常

     为了解决这一问题，我们可以在方法参数中添加@RequestParam注解，
     来指定参数的名称和是否必须携带该参数，以及参数的默认值等内容；
     也可以通过将方法参数类型设置为基本数据的包装类型，而非基本数据类型，
     如此就能接收为null的值

     其实在默认情况下我们不加@RequestParam注解，
     也可以将请求参数绑定到方法参数中，因为存在默认的@RequestParam注解，
     只是在默认情况下，该注解的required属性为true，即必须携带该参数；
     而在我们的案例中，name参数是可选的，所以我们需要将required属性设置为false；
     */
    @RequestMapping("/index")
    public String index(@RequestParam(value = "name", required = false) String name,
                        @RequestParam(value = "age", required = false, defaultValue = "0") Integer age) {
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        return "index.html";
    }

    /*
        SpringMVC中Controller类内部处理方法接收参数的方式：
            1. 使用@RequestParam注解进行标注获取，那么能够直接获取请求参数中
            对应参数名的参数值，相当于使用请求对象调用其getParameter()方法，
            request.getParameter("方法的参数名")，在获取时会根据方法参数的类型进行类型转换；
            @RequestParam注解能够获取的数据都是在Tomcat等web服务器中处理好请求参数，
            包括get请求时放在路径上的参数，以及post请求时放在请求体中以key=value&k2=v2形式存储的数据
            由于@RequestParam注解获取的都是现成的以及处理完毕的请求参数数据，所以通常情况下，
            该注解可以省略不写，具体如下
                1.1 当请求参数中存在与方法参数名相同的参数时，
                那么就会将请求参数中的值绑定到方法参数中；
                1.2 当请求参数中不存在与方法参数名相同的参数时，
                那么就会将方法参数设置为null或默认值；
         2. 如果要获取请求头中携带的参数，那么需要使用@RequestHeader注解，
         在使用该注解时，通常都会指定要获取的请求头参数的key，根据key获取value值
         3. 如果要获取存储在请求体内以json格式或xml等其他格式存储的数据，
         由于tomcat等服务器默认不支持直接解析json格式或xml等其他格式的数据，
         所以在获取请求体中存储的json格式或xml等其他格式数据时，
         就需要我们手动解析请求体中的数据，然后将其转换为我们需要的对象；
         但是SpringMVC框架在整合了jackson等json解析库后，
         就可以直接将请求体中存储的json格式数据转换为我们需要的对象；
         但是本方式获取还是需要通过 @RequestBody注解进行标注，
         才能将请求体中的数据转换为我们需要的对象；如test2方法中所示
        4. 如果要获取请求路径中携带的参数，那么需要使用@PathVariable注解，
        在使用该注解时，通常都会指定要获取的路径参数的key，根据key获取value值，
        并将其绑定到方法参数中；如test3方法中所示；
     */
    @RequestMapping("/test")
    public String index2(String username, String password,
                         @RequestHeader("Accept") String accept) {
        System.out.println("username = " + username);
        System.out.println("password = " + password);
        System.out.println("accept = " + accept);
        return "index.html";
    }

    // 在@RequestMapping注解内，可以通过method属性指定本方法能够支持的请求方式
    // 如果不设置method属性，默认支持所有请求方式
    @RequestMapping(value = "/test2", method = RequestMethod.POST)
    // 在方法上添加@ResponseBody注解后，就会直接将方法的返回值放入到响应体中，
    // 而无需经过视图解析器渲染
    @ResponseBody
    // @RequestBody注解标注解析的是如json格式的数据，其通常都是一个对象。
    // 所以我们在接收该解析的数据时，也应该指定对象来接收，如User类所示；
    // 如此，SpringMVC框架就会调用如jackson框架更具该对象的类型解析请求体中的json格式数据，
    // 并将其转换为User对象，最后将该对象绑定到方法参数中；
    public String test2(@RequestBody User user) throws IOException {
        System.out.println("user = " + user);
        return "index.html";
    }

    // 有时，参数可能存在与请求路径中，如/test3/123/456
    // 那么在这种情况下，我们就需要使用@PathVariable注解来标注该参数，
    // 并指定参数名，如test3方法中所示；
    // 如此，SpringMVC框架就会将请求路径中对应参数名的参数值，绑定到方法参数中；
    @RequestMapping("/test3/{id}/{age}")
    @ResponseBody
    public String test3(@PathVariable String id, @PathVariable Integer age) {
        System.out.println("id = " + id);
        System.out.println("age = " + age);
        return "index.html";
    }
}
